package controllers

import (
	"context"
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/zmqge/vireo-gin-admin/app/admin/services"
	"github.com/zmqge/vireo-gin-admin/config"
	"github.com/zmqge/vireo-gin-admin/pkg/response"
)

// FileController File控制器
// @Group(path="/api/v1/", name="File管理")
type FileController struct {
	service services.FileService
}

// NewFileController 创建File控制器
func NewFileController(service services.FileService) *FileController {
	return &FileController{service: service}
}

// getFile 获取单个File
// @Route(method=GET, path="/file/:id", middlewares=["jwt","dataperm"])
// @Permission(code="sys:file:view", name="File详情",modules="File管理", desc="查看File详情")
func (c *FileController) GetFileDetails(ctx *gin.Context) {
	idStr := ctx.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		response.BadRequest(ctx, "Invalid ID")
		return
	}

	entity, err := c.service.GetFileByID(ctx, uint(id))
	if err != nil {
		response.Error(ctx, err)
		return
	}
	response.Success(ctx, entity)
}

// listFiles 获取File分页列表
// @Route(method=GET, path="/file/page", middlewares=["jwt","dataperm"])
// @Permission(code="sys:file:query",name="File列表",modules="File管理", desc="查看File列表")
func (c *FileController) ListFiles(ctx *gin.Context) {
	keywords := ctx.Query("keywords")
	pageNumStr := ctx.DefaultQuery("pageNum", "1")
	pageSizeStr := ctx.DefaultQuery("pageSize", "10")
	pageNum, _ := strconv.Atoi(pageNumStr)
	pageSize, _ := strconv.Atoi(pageSizeStr)
	if pageNum < 1 {
		pageNum = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}

	list, total, err := c.service.PageFiles(ctx, keywords, pageNum, pageSize)
	if err != nil {
		response.Error(ctx, err)
		return
	}
	resp := map[string]interface{}{
		"list":  list,
		"total": total,
	}
	response.Success(ctx, resp)
}

// createFile 上传文件
// @Route(method=POST, path="/files", middlewares=["jwt"])
// @Permission(code="sys:file:add",name="新建File",modules="File管理", desc="创建File")
func (c *FileController) UploadFile(ctx *gin.Context) {
	// 1. 从 gin.Context 获取上下文并派生
	timeoutCtx, cancel := context.WithTimeout(ctx.Request.Context(), 2*time.Minute)
	defer cancel()

	// 将新的上下文设置到请求中（虽然这不是必须的，因为我们会直接使用 timeoutCtx）
	ctx.Request = ctx.Request.WithContext(timeoutCtx) // 这行实际上不是必须的

	fileHeader, err := ctx.FormFile("file")
	if err != nil {
		response.BadRequest(ctx, err.Error())
		return
	}

	file, err := c.service.UploadFile(ctx, fileHeader)
	if err != nil {
		response.Error(ctx, err)
		return
	}

	response.Success(ctx, file)
}

// 异步上传文件 （未启用）
func (c *FileController) AsyncCreateFile(ctx *gin.Context) {
	// 调试日志：记录请求开始
	log.Printf("异步上传文件请求开始")

	// 设置最大上传大小为50MB
	// 从配置中获取最大上传大小
	maxSizeMB := config.App.FileUpload.MaxSizeMB
	maxSize := int64(maxSizeMB << 20)
	ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, maxSize)

	file, header, err := ctx.Request.FormFile("file")
	if err != nil {
		if strings.Contains(err.Error(), "request body too large") {
			response.BadRequest(ctx, fmt.Sprintf("文件大小超过限制(最大允许%dMB)", maxSize/(1<<20)))
			return
		}
		response.BadRequest(ctx, "请上传文件")
		return
	}
	defer file.Close()

	// 检查文件大小是否超过限制
	if header.Size > maxSize {
		response.BadRequest(ctx, fmt.Sprintf("文件大小(%dMB)超过限制(最大允许%dMB)",
			header.Size/(1<<20), maxSize/(1<<20)))
		return
	}

	// 读取文件前512字节用于检测文件类型
	buffer := make([]byte, 512)
	_, err = file.Read(buffer)
	if err != nil {
		log.Printf("读取文件头错误: %v", err)
		response.Error(ctx, err)
		return
	}

	// 重置文件指针
	file.Seek(0, 0)

	// 获取文件类型
	fileType := http.DetectContentType(buffer)
	log.Printf("检测到的文件类型: %s", fileType)

	// 检查文件类型是否允许
	allowed := false
	for _, allowedType := range config.App.FileUpload.AllowedTypes {
		if fileType == allowedType {
			allowed = true
			break
		}
	}
	if !allowed {
		log.Printf("不允许的文件类型: %s", fileType)
		response.BadRequest(ctx, fmt.Sprintf("不允许的文件类型: %s", fileType))
		return
	}

	// 计算文件的MD5和SHA256
	hashMD5 := md5.New()
	hashSHA256 := sha256.New()
	log.Printf("开始计算文件哈希值")
	multiWriter := io.MultiWriter(hashMD5, hashSHA256)

	// 复制文件内容到hash计算器
	if _, err := io.Copy(multiWriter, file); err != nil {
		response.Error(ctx, err)
		return
	}

	md5Sum := hex.EncodeToString(hashMD5.Sum(nil))
	sha256Sum := hex.EncodeToString(hashSHA256.Sum(nil))

	// 获取文件扩展名
	ext := filepath.Ext(header.Filename)

	response.Success(ctx, gin.H{
		"filename":  header.Filename,
		"size":      header.Size,
		"type":      fileType,
		"is_binary": !strings.HasPrefix(fileType, "text/"),
		"md5":       md5Sum,
		"sha256":    sha256Sum,
		"ext":       ext,
	})
}

// deleteFile 删除File
// @Route(method=DELETE, path="/file/:id", middlewares=["jwt"])
// @Permission(code="sys:file:delete",name="删除File",modules="File管理", desc="删除File")
func (c *FileController) DeleteFile(ctx *gin.Context) {
	idStr := ctx.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		response.BadRequest(ctx, "Invalid ID")
		return
	}

	err = c.service.DeleteFile(ctx, uint(id))
	if err != nil {
		response.Error(ctx, err)
		return
	}
	response.Success(ctx, nil)
}

// getDict 获取File表单
// @Route(method=GET, path="/file/:id/form", middlewares=["jwt","dataperm"])
// @Permission(code="sys:file:details",name="File详情",modules="File管理", desc="查看File详情")
func (c *FileController) GetFileForm(ctx *gin.Context) {
	idStr := ctx.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 64)
	if err != nil {
		response.BadRequest(ctx, "Invalid ID")
		return
	}
	entity, err := c.service.GetFileByID(ctx, uint(id))

	if err != nil {
		response.Error(ctx, err)
		return
	}
	response.Success(ctx, entity)
}
